package com.cmy.web.widget

import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.widget.FrameLayout
import android.widget.ProgressBar
import androidx.annotation.AttrRes
import com.cmy.web.WebViewState
import com.cmy.web.databinding.ViewWebX5Binding
import com.cmy.web.listener.*
import com.tencent.smtt.export.external.extension.proxy.ProxyWebChromeClientExtension
import com.tencent.smtt.export.external.interfaces.*
import com.tencent.smtt.sdk.*

/**
 * @name:
 *  BaseX5WebView
 * @package:
 *  com.cmy.web.widget
 * @date:
 *  2023/4/2114:37
 * @author
 *    cmy
 * @description
 *  打造基础类的x5内核
 */
open class BaseX5WebView constructor(context: Context, attrs: AttributeSet?,@AttrRes defStyleAttr:Int): FrameLayout(context,attrs,defStyleAttr),
    WebViewLifecycleListener, BaseWebViewListener , BaseWebViewExpandListener {
    constructor(context: Context, attrs: AttributeSet?):this(context,attrs,0)
    constructor(context: Context):this(context,null)

    private val binding by lazy {   ViewWebX5Binding.inflate(LayoutInflater.from(context)) }

    protected var isLoadError = false

    private val mWebView by lazy { binding.webView }

    private var mWebViewClient :BaseX5WebClient? = null

    private var mWebChromeClient :BaseX5WebChromeClient?=null

    private var mJsDialogListener : JsDialogListener? = null

    private var mInterceptListener: BaseViewInterceptListener? = null

    private var mJavascriptInterface : JavascriptInterface? = null

    private var mStateListener:WebViewStateListener? = null

    init {
        addView(binding.root)
        initWebView()
    }
    /**
     * 初始化
     */
    @SuppressLint("SuspiciousIndentation", "SetJavaScriptEnabled")
    protected open fun initWebView() {
        //------------------------
        mWebView.clearCache(true)
        mWebView.setLayerType(LAYER_TYPE_NONE, null)
        val webSettings: WebSettings = mWebView.settings
//        if (NetworkUtils.isConnected(getApplicationContext())) {
//            //根据cache-control决定是否从网络上取数据。
//            webSettings.cacheMode = WebSettings.LOAD_DEFAULT
//        } else {
//            //没网，则从本地获取，即离线加载
//            webSettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
//        }

            WebView.setWebContentsDebuggingEnabled(false)


//        webSettings.setDomStorageEnabled(true);
//
//        webSettings.setAppCacheMaxSize(1024*1024*8);
//
//        String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();
//
//        webSettings.setAppCachePath(appCachePath);
//
//        webSettings.setAllowFileAccess(true);
        webSettings.javaScriptEnabled = true
        webSettings.domStorageEnabled = true
        webSettings.databaseEnabled = true
        webSettings.setAppCacheEnabled(true)
        webSettings.useWideViewPort = true
        webSettings.setGeolocationEnabled(true)
        webSettings.loadWithOverviewMode = true
        webSettings.allowFileAccess = false
        webSettings.setSupportZoom(false)
        webSettings.builtInZoomControls = false
//        webSettings.allowFileAccessFromFileURLs = true
//        webSettings.allowUniversalAccessFromFileURLs = true
        webSettings.mediaPlaybackRequiresUserGesture = false
        //        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);//支持内容重新布局
        webSettings.mixedContentMode = WebSettings.LOAD_DEFAULT
        mWebView.settings.defaultTextEncodingName = "gb2312"
        mWebViewClient = BaseX5WebClient(binding.pbLoading)
        mWebChromeClient = BaseX5WebChromeClient(binding.pbLoading)
        mWebView.webChromeClientExtension = BaseX5ProxyWebChromeClientExtension()
        mWebView.webChromeClient = mWebChromeClient
        mWebView.webViewClient = mWebViewClient
//        mWebView.setDefaultHandler(DefaultHandler())
        mWebView.overScrollMode = OVER_SCROLL_NEVER
//        mWebView.setBackgroundColor(ContextCompat.getColor(context, R.color.background))
//        val ua = webSettings.userAgentString


//        //获取密码接口
//        mWebView.getHttpAuthUsernamePassword(host, realm)
//        //保存密码接口
//        mWebView.setHttpAuthUsernamePassword(String host, String realm,String username, String password)

        //可以关闭定位功能，内核默认是开启的
        webSettings.setGeolocationEnabled(false)

        // enable:true(日间模式)，enable：false（夜间模式）
//        mWebView.getSettingsExtension().setDayOrNight(eanble)
        // 对于刘海屏机器如果webview被遮挡会自动padding
//        mWebView.getSettingsExtension().setDisplayCutoutEnable(true)
        //无痕模式
//        mWebView.getSettingsExtension().setShouldTrackVisitedLinks(enable)
        //强制缩放
//        mWebView.getSettingsExtension().setShouldTrackVisitedLinks(enable)
        //前进后退缓存
//        mWebView.getSettingsExtension().setContentCacheEnable(true);
        Log.e("TAG", "UA:" + webSettings.userAgentString)

        if (mJavascriptInterface != null){
            mWebView.addJavascriptInterface(mJavascriptInterface!!.getInterfaceClass(), mJavascriptInterface!!.getInterfaceName())
        }
        mWebView.clearFocus()

    }
    /**
     * x5对应的WebViewClient
     */
    inner class BaseX5WebClient  constructor(loading: ProgressBar):WebViewClient(){

        private val pbLoading = loading

        override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
            //TODO 这里可以对特殊scheme进行拦截处理,要返回true否则内核会继续处理
            return if (mInterceptListener != null){
                 mInterceptListener!!.shouldOverrideUrlLoading(url)
            }else{
                super.shouldOverrideUrlLoading(view, url)
            }
        }

        override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
            // TODO 这里可以对特殊scheme进行拦截处理,要返回true否则内核会继续处理

            return if (mInterceptListener != null){
                mInterceptListener!!.shouldOverrideUrlLoading(request?.url.toString())
            }else{
                super.shouldOverrideUrlLoading(view, request)
            }
        }

        /**
         * 当访问页面需要输入用户名和密码时会回调对应client接口
         */
        override fun onReceivedHttpAuthRequest(
            view: WebView?,
            handler: HttpAuthHandler?,
            host: String?,
            realm: String?
        ) {
            super.onReceivedHttpAuthRequest(view, handler, host, realm)
            //TODO 首先判断是否可以重复使用对应用户名和密码如果可以则获取已保存的密码（获取成功后直接使用）,如果不允许重复使用用户名和密码或者未保存用户名和密码则需要提示用户输入,用户输入用户名和密码后可以将对应数据保存

        }


        override fun onPageFinished(view: WebView, url: String?) {
            super.onPageFinished(view, url)
            pbLoading.visibility = GONE
            pbLoading.progress = 0


            if (mStateListener != null){
                mStateListener!!.onState(if (isLoadError) WebViewState.LoadError else WebViewState.LoadComplete)
            }


        }

        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
            super.onPageStarted(view, url, favicon)
            pbLoading.visibility = VISIBLE

            if (mStateListener != null){
                mStateListener!!.onState(WebViewState.Loading)
            }
        }


        // 旧版本，会在新版本中也可能被调用，所以加上一个判断，防止重复显示
        override fun onReceivedError(
            view: WebView?,
            errorCode: Int,
            description: String?,
            failingUrl: String?
        ) {
            super.onReceivedError(view, errorCode, description, failingUrl)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                return
            }
            // 在这里显示自定义错误页
            isLoadError = true

        }

        // 新版本，只会在Android6及以上调用
        @TargetApi(Build.VERSION_CODES.M)
        override fun onReceivedError(
            view: WebView?,
            request: WebResourceRequest,
            error: WebResourceError?
        ) {
            super.onReceivedError(view, request, error)
            if (request.isForMainFrame) { // 或者： if(request.getUrl().toString() .equals(getUrl()))
                // 在这里显示自定义错误页
                isLoadError = true
            }
        }


    }
    /**
     * x5对应的WebChromeClient
     */
    inner class BaseX5WebChromeClient constructor(loading: ProgressBar):WebChromeClient(){
        private val pbLoading: ProgressBar = loading

        override fun onJsAlert(webview: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
            //TODO  可以弹框或进行其它处理，但一定要回调result.confirm或者cancel 这里要返回true否则内核会进行提示
            return if (mJsDialogListener != null){
                mJsDialogListener!!.onJsAlert(url,message)
            }else{
                super.onJsAlert(webview, url,message, result)
            }
        }

        override fun onJsConfirm(webview: WebView?, url: String?, message: String?, result: JsResult?): Boolean{
            //TODO 可以弹框或进行其它处理，但一定要回调result.confirm或者cancel
            return if (mJsDialogListener != null){
                mJsDialogListener!!.onJsConfirm(url,message)
            }else{
                super.onJsConfirm(webview, url,message, result)
            }
        }


        override fun onJsBeforeUnload(webview: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
            //TODO 可以弹框或进行其它处理，但一定要回调result.confirm或者cancel
            return if (mJsDialogListener != null){
                mJsDialogListener!!.onJsBeforeUnload(url,message)
            }else{
                super.onJsBeforeUnload(webview, url,message, result)
            }
        }

        override fun onJsPrompt(
            webview: WebView?,
            url: String?,
            message: String?,
            defaultvalue: String?,
            result: JsPromptResult?
        ): Boolean {
            //TODO 可以弹框或进行其它处理，但一定要回调result.confirm或者cancel，confirm可以将用户输入作为参数
            return if (mJsDialogListener != null){
                mJsDialogListener!!.onJsPrompt(url,message,defaultvalue)
            }else{
                super.onJsPrompt(webview, url, message, defaultvalue, result)
            }
        }

        /**
         * 当用户请求定位时会回调对应client的授权接口
         */
        override fun onGeolocationPermissionsShowPrompt(
            origin: String?,
            callback: GeolocationPermissionsCallback?
        ) {
            //TODO 在此可以弹窗提示用户 ,处理后需要回调 ,参数的意义见上面的接口说明
            callback?.invoke(origin,true,false)
//            super.onGeolocationPermissionsShowPrompt(origin, callback)
        }


        /**
         * 文件单选：设置client回调
         */
        override fun openFileChooser(uploadFile: ValueCallback<Uri>?, acceptType: String?, captureType: String?) {
            super.openFileChooser(uploadFile, acceptType, captureType)
            //TODO 保存对应的valuecallback供选择后使用,通过startActivityForResult启动文件选择窗口或自定义文件选择
        }


        /**
         * 设置client回调（单选多选均会回调该接口）
         */
        override fun onShowFileChooser(
            webView: WebView?,
            filePathCallback: ValueCallback<Array<Uri>>?,
            fileChooserParams: FileChooserParams?
        ): Boolean {
            //TODO 保存对应的valuecallback供选择后使用,通过startActivityForResult启动文件选择窗口或自定义文件选择
            return super.onShowFileChooser(webView, filePathCallback, fileChooserParams)
        }

        override fun onProgressChanged(view: WebView?, newProgress: Int) {
            super.onProgressChanged(view, newProgress)
            if (newProgress < 100){
                pbLoading.progress = newProgress

                if (mStateListener != null){
                    mStateListener!!.onState(WebViewState.Loading)
                }
            }
        }
    }

    inner class BaseX5ProxyWebChromeClientExtension: ProxyWebChromeClientExtension(){
        /**
         * 文件多选：设置client回调
         */
        override fun openFileChooser(
            uploadFile: android.webkit.ValueCallback<Array<Uri>>?,
            acceptType: String?,
            captureType: String?
        ) {
            super.openFileChooser(uploadFile, acceptType, captureType)
            //TODO 保存对应的valuecallback供选择后使用,通过startActivityForResult启动文件选择窗口或自定义文件选择
        }


        /**
         * 前进后退结束回调(页面前进后退切换完成事件通知，目前step无实际赋值，此接口只是一个完成通知)
         */
        override fun onBackforwardFinished(p0: Int) {
            super.onBackforwardFinished(p0)
        }
    }

    //--------------------------公共调用-----------------------------------

    /**
     * 返回对应的WebView视图，便于拓展
     */
    fun getWebView():WebView{
        return mWebView
    }

    //------------------------实现各类生命周期类----------------------------



    override fun onResume() {
       mWebView.onResume()
    }

    override fun onPause() {
        mWebView.onPause()
    }

    override fun onDestroyView() {
            cleanListener()
            mWebView.visibility = GONE
            mWebView.stopLoading()
            // 退出时调用此方法，移除绑定的服务，否则某些特定系统会报错
            mWebView.settings.javaScriptEnabled = false
            mWebView.clearHistory()
            mWebView.removeAllViews()
            mWebView.destroy()
    }

    override fun onBackPressed(): Boolean {
        return if (mWebView.canGoBack()) {
            mWebView.goBack()
            true
        } else {
            false
        }
    }

    override fun loadUrl(url: String) {
        isLoadError = false
        mWebView.loadUrl(url)

    }

    override fun loadDataWithBaseURL(
        baseUrl: String,
        data: String,
        mimeType: String,
        encoding: String,
        failUrl: String
    ) {
        isLoadError = false
        mWebView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, failUrl)
    }


    override fun evaluateJavascript(script: String, result: (String?) -> Unit) {
        mWebView.evaluateJavascript(script,result)
    }


    override fun evaluateJavascript(script: String, resultCallback: CallBackFunction) {
        mWebView.evaluateJavascript(script, ValueCallback {
            resultCallback.onCallBack(it)
        })
    }


    override fun canGoBack() :Boolean{
        return mWebView.canGoBack()
    }

    override fun goBack() {
        mWebView.goBack()
    }

    override fun registerWebViewStateListener(stateListener: WebViewStateListener) {
        this.mStateListener = stateListener
    }

    override fun addJsDialogListener(jsDialogListener: JsDialogListener) {
        this.mJsDialogListener = jsDialogListener
    }

    override fun addInterceptListener(interceptListener: BaseViewInterceptListener) {
        this.mInterceptListener = interceptListener
    }

    override fun addJavascriptInterface(javascriptInterface: JavascriptInterface) {
        this.mJavascriptInterface = javascriptInterface
    }


    /**
     * 清理数据，防止内存泄漏
     */
    private fun cleanListener(){
        if (mJsDialogListener != null){
            mJsDialogListener = null
        }

        if (mJavascriptInterface != null){
            mJavascriptInterface = null
        }

        if (mStateListener != null){
            mStateListener = null
        }


        if (mInterceptListener != null){
            mInterceptListener = null
        }


        if (mWebViewClient != null){
            mWebViewClient = null
        }

        if (mWebChromeClient != null){
            mWebChromeClient = null
        }

    }
}